/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ohosannotations.internal.core.handler;

import com.helger.jcodemodel.AbstractJClass;
import com.helger.jcodemodel.IJExpression;
import com.helger.jcodemodel.JBlock;
import com.helger.jcodemodel.JCatchBlock;
import com.helger.jcodemodel.JExpr;
import com.helger.jcodemodel.JInvocation;
import com.helger.jcodemodel.JMethod;
import com.helger.jcodemodel.JTryBlock;
import com.helger.jcodemodel.JVar;

import org.ohosannotations.ElementValidation;
import org.ohosannotations.OhosAnnotationsEnvironment;
import org.ohosannotations.annotations.Transactional;
import org.ohosannotations.handler.BaseAnnotationHandler;
import org.ohosannotations.helper.CanonicalNameConstants;
import org.ohosannotations.holder.EComponentHolder;

import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;

/**
 * 事务处理程序
 *
 * @author dev
 * @since 2021-07-22
 */
public class TransactionalHandler extends BaseAnnotationHandler<EComponentHolder> {
    /**
     * 事务处理程序
     *
     * @param environment 环境
     */
    public TransactionalHandler(OhosAnnotationsEnvironment environment) {
        super(Transactional.class, environment);
    }

    /**
     * 验证
     *
     * @param element 元素
     * @param validation 验证
     */
    @Override
    public void validate(Element element, ElementValidation validation) {
        validatorHelper.enclosingElementHasEnhancedComponentAnnotation(element, validation);

        validatorHelper.isNotPrivate(element, validation);

        ExecutableElement executableElement = (ExecutableElement) element;

        validatorHelper.doesntThrowException(executableElement, validation);

        validatorHelper.isNotFinal(element, validation);

        validatorHelper.param.inOrder() //
            .type(CanonicalNameConstants.SQLITE_DATABASE) //
            .anyType().multiple().optional() //
            .validate(executableElement, validation);
    }

    /**
     * 过程
     *
     * @param element 元素
     * @param holder 持有人
     */
    @Override
    public void process(Element element, EComponentHolder holder) {
        ExecutableElement executableElement = (ExecutableElement) element;

        String returnTypeName = executableElement.getReturnType().toString();
        AbstractJClass returnType = getJClass(returnTypeName);

        JMethod method = codeModelHelper.overrideAnnotatedMethod(executableElement, holder);
        codeModelHelper.removeBody(method);

        JVar db = method.params().get(0);

        JBlock body = method.body();

        body.invoke(db, "beginTransaction");

        JTryBlock tryBlock = body._try();

        IJExpression abilitySuper = holder.getGeneratedClass().staticRef("super");
        JInvocation superCall = JExpr.invoke(abilitySuper, method);

        for (JVar param : method.params()) {
            superCall.arg(param);
        }
        JBlock tryBody = tryBlock.body();
        if ("void".equals(returnTypeName)) {
            tryBody.add(superCall);
            tryBody._return();
        } else {
            JVar result = tryBody.decl(returnType, "result_", superCall);
            tryBody._return(result);
        }

        JCatchBlock catchBlock = tryBlock._catch(getJClass(RuntimeException.class));
        JVar exceptionParam = catchBlock.param("e");
        JBlock catchBody = catchBlock.body();

        catchBody._throw(exceptionParam);
        tryBlock._finally().invoke(db, "endTransaction");
    }
}
